﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.Text;
using MobySharp.Actions;
using MobySharp.Results;

namespace MobySharp
{
    public class Mobypicture
    {
        private const string POST_URL_PLAIN = "http://api.mobypicture.com";
        private const string POST_URL_SSL = "https://api.mobypicture.com";

        private readonly HTTPWorker _http = new HTTPWorker();
        private readonly string _key;

        /// <summary>
        /// Initializes a new instance of the <see cref="Mobypicture"/> class.
        /// </summary>
        /// <param name="key">The unique developer key. Obtain your own key at http://api.mobypicture.com</param>
        public Mobypicture(string key)
        {
            _key = key;
            InitWorker(false);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Mobypicture"/> class.
        /// </summary>
        /// <param name="key">The unique developer key. Obtain your own key at http://api.mobypicture.com</param>
        /// <param name="useSSL">if set to <c>true</c>, SSL will be used.</param>
        public Mobypicture(string key, bool useSSL)
        {
            _key = key;
            InitWorker(useSSL);
        }

        private void InitWorker(bool secure)
        {
            _http.Url = secure ? POST_URL_SSL : POST_URL_PLAIN;
            _http.RequestObject.KeepAlive = true;
            _http.RequestObject.Headers.Add("Pragma", "no-cache");
        }


        /// <summary>
        /// Checks whether the user is a valid Mobypicture user. Unless specified otherwise, it will -in case of a non-existing user on Mobypicture- also check whether the supplied credentials are belonging to a Twitter-user. If so, it will automatically create a Mobypicture for it.
        /// </summary>
        /// <param name="action">The CheckCredentials action.</param>
        /// <returns></returns>
        public BasicResult CheckCredentials(CheckCredentials action)
        {
            return (BasicResult) BaseActionExecution(action);
        }

        /// <summary>
        /// Creates the Mobypicture user.
        /// </summary>
        /// <param name="action">The CreateUser action.</param>
        /// <returns></returns>
        public BasicResult CreateUser(CreateUser action)
        {
            return (BasicResult) BaseActionExecution(action);
        }

        /// <summary>
        /// Retrieves all comments for a giving posting. In the GetComments action, use PostID OR TinyURLcode
        /// </summary>
        /// <param name="action">The GetComments action.</param>
        /// <returns></returns>
        public CommentsResult GetComments(GetComments action)
        {
            return (CommentsResult) BaseActionExecution(action);
        }

        /// <summary>
        /// Gets the friends for the given user.
        /// </summary>
        /// <param name="action">The GetFriends action.</param>
        /// <returns></returns>
        public FriendsResult GetFriends(GetFriends action)
        {
            return (FriendsResult) BaseActionExecution(action);
        }

        /// <summary>
        /// Returns information about a posting. Fields that are not available will be returned empty.
        /// </summary>
        /// <param name="action">The GetMediaInfo action.</param>
        /// <returns></returns>
        public MediaInfoResult GetMediaInfo(GetMediaInfo action)
        {
            return (MediaInfoResult) BaseActionExecution(action);
        }

        /// <summary>
        /// Get the location of the thumbnail version of the photo associated with a Mobypicture URL 
        /// </summary>
        /// <param name="action">The GetThumb action.</param>
        /// <returns></returns>
        public BasicResult GetThumb(GetThumb action)
        {
            return (BasicResult) BaseActionExecution(action);
        }

        /// <summary>
        /// Gets the user details of a Mobypicture user.
        /// </summary>
        /// <param name="action">The GetUser action.</param>
        /// <returns></returns>
        public UserResult GetUser(GetUser action)
        {
            return (UserResult) BaseActionExecution(action);
        }

        /// <summary>
        /// Posts a comment.
        /// </summary>
        /// <param name="action">The PostComment action.</param>
        /// <returns></returns>
        public BasicResult PostComment(PostComment action)
        {
            return (BasicResult) BaseActionExecution(action);
        }

        /// <summary>
        /// Posts the media. Currently only posting images is supported
        /// </summary>
        /// <param name="action">The action.</param>
        /// <returns></returns>
        public BasicResult PostMedia(PostMedia action)
        {
            return (BasicResult) BaseActionExecution(action);
        }


        /// <summary>
        /// Searches the Mobypicture posts.
        /// </summary>
        /// <param name="action">The search action.</param>
        /// <returns></returns>
        public SearchResult SearchPosts(SearchPosts action)
        {
            return (SearchResult) BaseActionExecution(action);
        }


        /// <summary>
        /// Adds the Like.
        /// </summary>
        /// <param name="action">The AddLike action.</param>
        /// <returns></returns>
        public LikeResult AddLike(AddLike action)
        {
            return (LikeResult) BaseActionExecution(action);
        }


        /// <summary>
        /// Gets the Likes.
        /// </summary>
        /// <param name="action">The AddLike action.</param>
        /// <returns></returns>
        public LikeResult GetLikes(GetLikes action)
        {
            return (LikeResult)BaseActionExecution(action);
        }

        /// <summary>
        /// Base action execution method.
        /// </summary>
        /// <param name="action">The action to execute.</param>
        /// <returns></returns>
        private IResult BaseActionExecution(IAction action)
        {
            // Set the Mobypicture developer key - Obtain
            action.Key = _key;

            _http.Type = action.RequestType;

            // Construct the http object and it validates the properties
            FillHTTPWorker(action);

            // Execute and get the response
            HttpWebResponse result = Execute();
            string response = GetResponse(result);

            if (result.StatusCode == HttpStatusCode.OK)
                return action.ParseResult(response);
            if (result.StatusCode == HttpStatusCode.Found)
                return new BasicResult
                           {
                               Url = result.Headers["Location"]
                           };

            return new BasicResult(-1);
        }

        /// <summary>
        /// Executes this instance.
        /// </summary>
        /// <returns></returns>
        private HttpWebResponse Execute()
        {
            HttpWebResponse rsp = null;
            try
            {
                rsp = _http.SendRequest();
            }
            catch (WebException ex)
            {
                Debug.WriteLine(ex.Message);
            }

            return rsp;
        }


        /// <summary>
        /// Gets the human readable response.
        /// </summary>
        /// <param name="response">The response.</param>
        /// <returns></returns>
        private string GetResponse(HttpWebResponse response)
        {
            Stream responseStream = response.GetResponseStream();
            var sb = new StringBuilder();
            var buf = new byte[8192];
            string tempString = null;
            int count = 0;

            do
            {
                // fill the buffer with data
                count = responseStream.Read(buf, 0, buf.Length);

                // make sure we read some data
                if (count != 0)
                {
                    // translate from bytes to ASCII text
                    tempString = Encoding.ASCII.GetString(buf, 0, count);

                    // continue building the string

                    sb.Append(tempString);
                }
            } while (count > 0); // any more data to read?

            responseStream.Close();
            return sb.ToString();
        }


        /// <summary>
        /// Fills the HTTP worker.
        /// </summary>
        /// <param name="postObject">The post object.</param>
        private void FillHTTPWorker(IAction postObject)
        {
            // Here we read out all the properties of the Mobypicture action
            // All properties that have a PostDataInstruction will be used for building
            // a http request. The PostDataInstruction contains information wether a
            // property is required and/or has a maximum length.
            
            PropertyInfo[] properties = postObject.GetType().GetProperties();

            foreach (PropertyInfo propertyInfo in properties)
            {
                foreach (Attribute attr in propertyInfo.GetCustomAttributes(true))
                {
                    var instruction = attr as PostDataInstruction;
                    if (instruction != null)
                    {
                        string key = instruction.Fieldname;
                        string value = string.Empty;

                        Type t = propertyInfo.PropertyType;

                        if (t == typeof(string) || t == typeof(int) || t == typeof(long))
                        {
                            object val = propertyInfo.GetValue(postObject, null);
                            if (val != null)
                                value = val.ToString();
                        }
                        else if (t == typeof (List<string>))
                        {
                            object val = propertyInfo.GetValue(postObject, null);
                            if (val != null)
                                value = string.Join(",", ((List<string>) val).ToArray());
                        }
                        else if (t == typeof (APICall) ||
                                 t == typeof (FormatType) ||
                                 t == typeof (PostMedia.LatLong) ||
                                 t == typeof (ThumbSize))
                        {
                            object val = propertyInfo.GetValue(postObject, null);
                            if (val != null)
                                value = val.ToString();
                        }
                        else if (t == typeof (YesNo))
                        {
                            var val = (YesNo) propertyInfo.GetValue(postObject, null);
                            value = (val == YesNo.yes) ? "1" : "0";
                        }


                        if (instruction.Required && string.IsNullOrEmpty(value))
                            throw new Exception("The property " + propertyInfo.Name + " is required");

                        if (instruction.MaxLength != null && value.Length > instruction.MaxLength)
                            throw new Exception("The property " + propertyInfo.Name + " has a max length of " +
                                                instruction.MaxLength + ". The current value has a length of " +
                                                value.Length);

                        // Add the data
                        if (!string.IsNullOrEmpty(value))
                        {
                            // If it is the image/media to upload the use the AddFile method
                            if (key == "i")
                                _http.AddFile(key, value);
                            else
                                _http.AddValue(key, value);
                        }
                    }
                }
            }
        }


    }

    // ReSharper disable InconsistentNaming

    #region HTTPRequestType enum

    /// <summary>
    /// HTTP Method: GET, POST, or POST with files/attachments
    /// </summary>
    public enum HTTPRequestType
    {
        Get,
        Post,
        MultipartPost
    }

    #endregion

    #region APICall enum

    public enum APICall
    {
        postMedia,
        postMediaUrl,
        getThumb,
        getThumbUrl,
        getMediaInfo,
        checkCredentials,
        postComment,
        getComments,
        searchPosts,
        createUser,
        getUser,
        getFriends,
        addFriend,
        addLike,
        getLikes
    }

    #endregion

    #region FormatType enum

    public enum FormatType
    {
        plain,
        xml,
        json
    }

    #endregion

    #region Services enum

    internal enum Services
    {
        Twitter,
        Jaiku,
        Tumblr,
        Brightkite,
        Wordpress,
        Blogger,
        Livejournal,
        Facebook,
        Hyves,
        Flickr,
        Youtube,
        Vimeo
    }

    #endregion

    #region ThumbSize enum

    public enum ThumbSize
    {
        thumbnail,
        small,
        medium
    }

    #endregion

    #region YesNo enum

    public enum YesNo
    {
        yes,
        no
    }

    #endregion

    // ReSharper restore InconsistentNaming
}